﻿//-----------------------------------------------------------------------
// <copyright file="MainWindow.xaml.cs" company="Luboš Bretschnieder">
// Copyright (c) 2008 Luboš Bretschnieder, Licensed under the Apache License, Version 2.0.
// </copyright>
//-----------------------------------------------------------------------
namespace GraphAlgorithms
{
    using System;
    using System.Reflection;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Shapes;
    using Graphs;
    using Graphs.Algorithms;
    using Settings;

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {
        /// <summary>Processing algorithm.</summary>
        private IAlgorithm algorithm = new Articulation();

        /// <summary>Last step index.</summary>
        private int lastStepIndex;

        /// <summary>Current step index.</summary>
        private int currentStepIndex;

        /// <summary>Current processed graph.</summary>
        private Graph currentGraph;

        /// <summary>Status window.</summary>
        private StatusWindow statusWindow;

        /// <summary>
        /// Initializes a new instance of the <see cref="MainWindow"/> class.
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();

            SizeChanged += this.MainWindow_SizeChanged;
            Closed += this.MainWindow_Closed;

            TbVerices.Text = "A,B,C,D,E,F,G,H,I";
            TbEdges.Text = "A-B;A-C;B-C;B-F;D-E;D-G;D-H;E-G;E-H;F-G;F-I;G-I";
            this.Title = this.Title + string.Format(" ({0})", Assembly.GetExecutingAssembly().GetName().Version);
        }

        /// <summary>
        /// Handles the Closed event of the MainWindow control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void MainWindow_Closed(object sender, EventArgs e)
        {
            if (this.statusWindow != null)
            {
                this.statusWindow.Close();
            }
        }

        /// <summary>
        /// Handles the SizeChanged event of the MainWindow control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.SizeChangedEventArgs"/> instance containing the event data.</param>
        private void MainWindow_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (this.currentGraph != null)
            {
                this.DrawGraph();
            }
        }

        /// <summary>
        /// Handles the Click event of the BtnInitialize control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void BtnInitialize_Click(object sender, RoutedEventArgs e)
        {
            Graph graph = new Graph();

            this.algorithm.Graph = graph;

            StepSlider.Value = 0;
            this.currentStepIndex = 0;
            StepSlider.IsEnabled = true;
            BtnNextStep.IsEnabled = true;

            try
            {
                string[] vertices = TbVerices.Text.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var vertexName in vertices)
                {
                    Vertex vertex = new Vertex(vertexName);
                    graph.Vertices.Add(vertex);
                    graph.SortedVertices.Add(vertexName, vertex);
                }

                string[] edges = TbEdges.Text.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var edge in edges)
                {
                    string[] verts = edge.Split(new[] { ',', '-' }, StringSplitOptions.RemoveEmptyEntries);
                    if (verts.Length != 2)
                    {
                        throw new ArgumentException("Edge has to be between exactly two vertices.");
                    }

                    graph.Edges.Add(new Edge(graph.GetVertex(verts[0]), graph.GetVertex(verts[1])));
                }

                this.currentGraph = graph;
                this.DrawGraph();

                // Run the algorithm
                this.algorithm.Execute(TbStartIn.Text);
                StepSlider.Maximum = this.lastStepIndex = this.algorithm.Steps.Count - 1;
                this.currentStepIndex = 0;

                if (this.algorithm.Steps.Count > 0)
                {
                    this.currentGraph = this.algorithm.Steps[0].Graph;
                    this.DrawGraph();
                    this.CheckButtonsVisibility();

                    if (this.statusWindow != null && this.statusWindow.IsVisible)
                    {
                        this.statusWindow.Close();
                    }

                    this.statusWindow = new StatusWindow(this.algorithm.Steps[0].AdditionalStatus);
                    this.statusWindow.Show();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(
                    string.Format("There is something wrong with vertices or edges definition: {0}", ex.Message),
                    "Error",
                    MessageBoxButton.OK,
                    MessageBoxImage.Error);
            }
        }

        /// <summary>
        /// Counts the positions.
        /// </summary>
        private void CountPositions()
        {
            Graph graph = this.currentGraph;
            if (graph.Vertices.Count > 0)
            {
                double radiusW = canvas.ActualWidth / 2;
                double radiusH = canvas.ActualHeight / 2;
                Point center = new Point(radiusW, radiusH);
                double additionAngle = Math.PI * 2 / graph.Vertices.Count;
                double currentAngle = -additionAngle;

                foreach (Vertex vertex in graph.Vertices)
                {
                    currentAngle += additionAngle;

                    double additionX = Math.Cos(currentAngle) * radiusW;
                    double additionY = Math.Sin(currentAngle) * radiusH;
                    vertex.X = center.X + additionX;
                    vertex.Y = center.Y + additionY;
                }
            }
        }

        /// <summary>
        /// Draws the graph.
        /// </summary>
        private void DrawGraph()
        {
            this.CountPositions();

            double radiusW = canvas.ActualWidth / 2;
            double radiusH = canvas.ActualHeight / 2;
            Point center = new Point(radiusW, radiusH);
            const double EllipseWidth = 15;
            canvas.Children.Clear();
            BrushConverter bc = new BrushConverter();

            if (this.currentGraph.Edges.Count > 0)
            {
                // draw edges
                foreach (Edge edge in this.currentGraph.Edges)
                {
                    Line l = new Line
                                {
                                    X1 = edge.From.X + (EllipseWidth / 2),
                                    Y1 = edge.From.Y + (EllipseWidth / 2),
                                    X2 = edge.To.X + (EllipseWidth / 2),
                                    Y2 = edge.To.Y + (EllipseWidth / 2),
                                    Stroke = ((Brush)bc.ConvertFrom("#" + ColorMapping.EdgeMapping[edge.State])),
                                    StrokeThickness = 2
                                };

                    canvas.Children.Add(l);
                }
            }

            if (this.currentGraph.Vertices.Count > 0)
            {
                // draw vertices
                foreach (Vertex vertex in this.currentGraph.Vertices)
                {
                    Ellipse e = new Ellipse
                                    {
                                        Width = EllipseWidth,
                                        Height = EllipseWidth,
                                        Fill = ((Brush)bc.ConvertFrom("#" + string.Format("{0:X6}", ColorMapping.VertexMapping[vertex.State])))
                                    };
                    Canvas.SetLeft(e, vertex.X);
                    Canvas.SetTop(e, vertex.Y);

                    canvas.Children.Add(e);
                }

                // draw labels
                foreach (Vertex vertex in this.currentGraph.Vertices)
                {
                    Label lbl = new Label
                                    {
                                        Content = string.Format("{0} ({1},{2})", vertex.Name, vertex.Order, vertex.Minimum),
                                        FontSize = 12,
                                        Foreground = ((Brush)bc.ConvertFrom("#" + string.Format("{0:X6}", ColorMapping.VertexMapping[vertex.State])))
                                    };
                    lbl.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
                    lbl.Arrange(new Rect(lbl.DesiredSize));

                    if (vertex.X > center.X && vertex.Y > center.Y)
                    {
                        Canvas.SetLeft(lbl, vertex.X + (EllipseWidth / 2));
                        Canvas.SetTop(lbl, vertex.Y + (EllipseWidth / 2));
                    }
                    else if (vertex.X > center.X && vertex.Y < center.Y)
                    {
                        Canvas.SetLeft(lbl, vertex.X + (EllipseWidth / 2));
                        Canvas.SetTop(lbl, vertex.Y - (EllipseWidth / 2) - (lbl.ActualHeight / 2));
                    }
                    else if (vertex.X < center.X && vertex.Y < center.Y)
                    {
                        Canvas.SetLeft(lbl, vertex.X - (EllipseWidth / 2) - lbl.ActualWidth);
                        Canvas.SetTop(lbl, vertex.Y - (EllipseWidth / 2) - (lbl.ActualHeight / 2));
                    }
                    else
                    {
                        Canvas.SetLeft(lbl, vertex.X - (EllipseWidth / 2) - lbl.ActualWidth);
                        Canvas.SetTop(lbl, vertex.Y + (EllipseWidth / 2));
                    }

                    canvas.Children.Add(lbl);
                }
            }
        }

        /// <summary>
        /// Makes step in the GUI.
        /// </summary>
        private void MakeGuiStep()
        {
            this.currentGraph = this.algorithm.Steps[++this.currentStepIndex].Graph;
            this.DrawGraph();

            StepSlider.Value = this.currentStepIndex;
        }

        /// <summary>
        /// Handles the ValueChanged event of the stepSlider control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The System.Windows.RoutedPropertyChangedEventArgs&lt;System.Double&gt; instance containing the event data.</param>
        private void StepSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (this.algorithm.Steps != null && this.algorithm.Steps.Count > 0)
            {
                this.currentGraph = this.algorithm.Steps[(int)e.NewValue].Graph;
                this.DrawGraph();

                this.statusWindow.StatusBars = this.algorithm.Steps[(int) e.NewValue].AdditionalStatus;
                this.statusWindow.UpdateStatus();
            }

            this.CheckButtonsVisibility();
            this.StepSlider.Focus();
        }

        /// <summary>
        /// Checks the buttons visibility.
        /// </summary>
        private void CheckButtonsVisibility()
        {
            BtnPrevStep.IsEnabled = StepSlider.Value != StepSlider.Minimum;
            BtnNextStep.IsEnabled = StepSlider.Value != StepSlider.Maximum;
        }

        /// <summary>
        /// Handles the Click event of the BtnPrevStep control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void BtnPrevStep_Click(object sender, RoutedEventArgs e)
        {
            StepSlider.Value -= 1;
        }

        /// <summary>
        /// Handles the Click event of the BtnNextStep control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void BtnNextStep_Click(object sender, RoutedEventArgs e)
        {
            StepSlider.Value += 1;
        }

        /// <summary>
        /// Handles the Click event of the MenuItemExit control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void MenuItemExit_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// Handles the Click event of the algortihm menu items.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void MenuItemAlgorithm_Click(object sender, RoutedEventArgs e)
        {
            string tag = ((MenuItem) sender).Tag.ToString();
            switch (tag)
            {
                case "Articulation":
                    this.algorithm = new Articulation();
                    break;
                case "Block":
                    this.algorithm = new Block();
                    break;
                case "DepthFirstSearch":
                    this.algorithm = new DepthFirstSearch();
                    break;
                case "BreadthFirstSearch":
                    this.algorithm = new BreadthFirstSearch();
                    break;
            }

            this.BtnInitialize_Click(BtnInitialize, null);

            foreach (MenuItem menuItem in MenuItemAlgorithm.Items)
            {
                menuItem.IsChecked = menuItem.Tag.ToString() == tag;
            }
        }

        /// <summary>
        /// Handles the Click event of the MenuItemAbout control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void MenuItemAbout_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Luboš Bretschneider, 2009 (bretik@gmail.com)\nhttp://code.google.com/p/wpfgraphalgorithms", "About", MessageBoxButton.OK, MessageBoxImage.Information);
        }
    }
}